///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file input.cpp
///
/// \brief   file holding implementations of functions associated with reading user input
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <src/pcps.h>
#include <src/mpi_interface.h>
#include <src/input.h>

/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief   Reads input lines from the specified file, converts the lines into input commands,
/////          uses the commands to set the values of the class's data members, and checks the
/////          input for sanity.
/////
///// \param[in]     filename  The name of the file holding the user's input options.
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//void pcps::Input::initialize_from_file(const std::string & filename) {
//
//  // get MPI info
//  const int nproc  = pcps::mpi::size();
//  const int myrank = pcps::mpi::rank();
//
//  // create a vector to hold the lines of the input file
//  std::vector<std::string> lines;
//
//  // create a vector to hold the characters of user input commands
//  std::vector<char> cmd;
//
//  // read commands from the input file on the root process
//  if (myrank == 0) {
//
//    // open the input file
//    std::ifstream input_file(filename.c_str());
//
//    // check that the file opened successfully
//    if (!input_file.is_open())
//      throw pcps::Exception("failed to open input file \"%s\"") % filename.c_str();
//
//    // print a greeting for the user input section of the output file
//    std::cout << "*********************************** Begin User Input ***********************************" << std::endl;
//    std::cout << std::endl;
//
//    // read and store the lines of the input file
//    while (!input_file.eof()) {
//
//      // create a string to hold the next line
//      std::string line;
//
//      // read the line (this discards the end-of-line character)
//      std::getline(input_file, line);
//
//      // remove comments from the line (all text after a % character)
//      std::stringstream ss(line);
//      line.clear();
//      std::getline(ss, line, '%');
//
//      // put in the end-of-line character
//      line.push_back('\n');
//
//      // print the line
//      std::cout << boost::format("line %4i:   %s") % (lines.size()+1) % line;
//
//      // ignore equal signs by replacing them with spaces
//      for (std::string::iterator c = line.begin(); c != line.end(); c++)
//        if (*c == '=')
//          *c = ' ';
//
//      // store the line
//      lines.push_back(line);
//
//    }
//
//    // print a banner ending the user input section of the output file
//    std::cout << std::endl;
//    std::cout << "***********************************  End User Input  ***********************************" << std::endl;
//    std::cout << std::endl;
//
//    // close the input file
//    input_file.close();
//
//    // read and process commands from the user's input, which are separated by ';' characters
//    for (int i = 0; i < lines.size(); i++) {
//
//      // convert the input line into a string stream
//      std::stringstream ss(lines.at(i));
//
//      // read characters from the stream
//      while (true) {
//
//        // read a character
//        char c; ss.get(c);
//
//        // if the stream is exhausted, move to the next line
//        if (ss.eof()) break;
//
//        // if we have reached the end of a command, process it
//        if (c == ';') {
//
//          // process the command (root node first, so it throws any errors)
//          this->process_input_command(cmd, i+1);
//
//          // send the command to the other processes
//          this->bcast_input_command(cmd, i+1);
//
//          // clear the characters in preparation for the next command
//          cmd.clear();
//
//        // otherwise, append the command with the character
//        } else {
//
//          cmd.push_back(c);
//
//        }
//
//      }
//
//    }
//
//    if (lines.size() > 0) {
//
//      // process the final command
//      this->process_input_command(cmd, lines.size()-1);
//
//      // send the final command to the other processes
//      this->bcast_input_command(cmd, lines.size()-1);
//
//    }
//
//    // signal the other processes to stop receiving input data
//    int stop_signal = -1;
//    pcps::mpi::bcast(stop_signal);
//
//    // verify that the input is sane
//    this->verify();
//
//  // slave processes
//  } else {
//
//    // receive input commands from the root process
//    while (true) {
//
//      // receive the line number on which the command ended
//      int line_num;
//      pcps::mpi::bcast(line_num);
//
//      // if the stop signal was sent, exit the loop
//      if (line_num < 0) break;
//
//      // receive the command
//      pcps::mpi::bcast(cmd);
//
//      // process the command
//      this->process_input_command(cmd, line_num);
//
//    }
//
//  }
//
//  // prepare derived data
//  this->initialize_derived_data();
//
//  // print the global random seed
//  if (myrank == 0)
//    std::cout << boost::format("global_seed set to ") << _global_seed << std::endl;
//
//  //// print out a summary of the input
//  //if (myrank == 0) {
//  //  std::cout << std::endl;
//  //  std::cout << boost::format("pcps program is being run with the following options:") << std::endl;
//  //  std::cout << std::endl;
//  //  std::cout << boost::format("                    workdir = ") << _workdir << std::endl;
//  //  std::cout << boost::format("              parallel_type = ") << _parallel_type << std::endl;
//  //  std::cout << boost::format("               corr_sources =");
//  //  for (std::set<std::string>::const_iterator s = _corr_sources.begin(); s != _corr_sources.end(); s++)
//  //    std::cout << " " << *s;
//  //  std::cout << std::endl;
//  //  std::cout << boost::format("                 orb_source = ") << _orb_source << std::endl;
//  //  std::cout << boost::format("       slightly_random_orbs = ") << ( _slightly_random_orbs ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("                 agp_source = ") << _agp_source << std::endl;
//  //  std::cout << boost::format("             pairing_source = ") << _pairing_source << std::endl;
//  //  std::cout << boost::format("               optimization = ") << _optimization << std::endl;
//  //  std::cout << boost::format("                   boundary = ") << _boundary << std::endl;
//  //  std::cout << boost::format("           hamiltonian_type = ") << _hamiltonian_type << std::endl;
//  //  std::cout << boost::format("                  hubbard_t = ") << _hubbard_t << std::endl;
//  //  std::cout << boost::format("                  hubbard_u = ") << _hubbard_u << std::endl;
//  //  std::cout << boost::format("               heisenberg_b = ") << _heisenberg_b << std::endl;
//  //  std::cout << boost::format("         active_energy_only = ") << ( _active_energy_only ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("                 nparticles = ") << _nparticles << std::endl;
//  //  std::cout << boost::format("              particle_type = ") << _particle_type << std::endl;
//  //  std::cout << boost::format("              particle_spin = ") << _particle_spin << std::endl;
//  //  std::cout << boost::format("                     z_spin = ") << _z_spin << std::endl;
//  //  std::cout << boost::format("                 project_sz = ") << ( _project_sz ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("                   ref_type = ") << _ref_type << std::endl;
//  //  std::cout << boost::format("                scalar_type = ") << _scalar_type << std::endl;
//  //  std::cout << boost::format("               lattice_type = ") << _lattice_type << std::endl;
//  //  std::cout << boost::format("           lattice_length_x = ") << _lattice_length_x << std::endl;
//  //  std::cout << boost::format("           lattice_length_y = ") << _lattice_length_y << std::endl;
//  //  std::cout << boost::format("           lattice_length_z = ") << _lattice_length_z << std::endl;
//  //  std::cout << boost::format("                  n_cor_orb = ") << _n_cor_orb << std::endl;
//  //  std::cout << boost::format("                  n_act_orb = ") << _n_act_orb << std::endl;
//  //  std::cout << boost::format("            correlator_type = ") << _correlator_type << std::endl;
//  //  std::cout << boost::format("            correlator_size = ") << _correlator_size << std::endl;
//  //  std::cout << boost::format("         random_correlators = ") << ( _random_correlators ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("              fix_classical = ") << ( _fix_classical ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("             optimize_corrs = ") << ( _optimize_corrs ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("       optimize_agp_weights = ") << ( _optimize_agp_weights ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("        optimize_orb_coeffs = ") << ( _optimize_orb_coeffs ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("    optimize_pairing_matrix = ") << ( _optimize_pairing_matrix ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("          symmetric_pairing = ") << ( _symmetric_pairing ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("                max_hf_iter = ") << _max_hf_iter << std::endl;
//  //  std::cout << boost::format("             max_macro_loop = ") << _max_macro_loop << std::endl;
//  //  std::cout << boost::format("                max_nr_iter = ") << _max_nr_iter << std::endl;
//  //  std::cout << boost::format("                    damping = %.6e") % _damping << std::endl;
//  //  std::cout << boost::format("           energy_tolerance = %.6e") % _energy_tolerance << std::endl;
//  //  std::cout << boost::format("                  step_size = ") << _step_size << std::endl;
//  //  std::cout << boost::format("                bfgs_length = ") << _bfgs_length << std::endl;
//  //  std::cout << boost::format("                  move_type = ") << _move_type << std::endl;
//  //  std::cout << boost::format("              sample_length = ") << _sample_length << std::endl;
//  //  std::cout << boost::format("             burn_in_length = ") << _burn_in_length << std::endl;
//  //  std::cout << boost::format("              max_grad_norm = ") << _max_grad_norm << std::endl;
//  //  std::cout << boost::format("                  dump_freq = ") << _dump_freq << std::endl;
//  //  std::cout << boost::format("                 print_freq = ") << _print_freq << std::endl;
//  //  std::cout << boost::format("            precondition_sr = ") << ( _precondition_sr ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("              overlap_shift = ") << _overlap_shift << std::endl;
//  //  std::cout << boost::format("                  cg_thresh = ") << _cg_thresh << std::endl;
//  //  std::cout << boost::format("                max_cg_iter = ") << _max_cg_iter << std::endl;
//  //  std::cout << boost::format("                  fci_shift = ") << _fci_shift << std::endl;
//  //  std::cout << boost::format("               trial_energy = %.12f") % _trial_energy << std::endl;
//  //  std::cout << boost::format("                    pmc_tau = ") << _pmc_tau << std::endl;
//  //  std::cout << boost::format("        walkers_per_process = ") << _walkers_per_process << std::endl;
//  //  std::cout << boost::format("           pop_control_freq = ") << _pop_control_freq << std::endl;
//  //  std::cout << boost::format("       begin_true_projector = ") << _begin_true_projector << std::endl;
//  //  std::cout << boost::format("         sr_disk_block_size = ") << _sr_disk_block_size << std::endl;
//  //  std::cout << boost::format("                sr_use_disk = ") << ( _sr_use_disk ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("      join_trotter_clusters = ") << ( _join_trotter_clusters ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("              verbose_print = ") << ( _verbose_print ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("       spin_to_fermion_dump = ") << ( _spin_to_fermion_dump ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("                 do_perturb = ") << ( _do_perturb ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("                 properties = ") << ( _properties ? "true" : "false" ) << std::endl;
//  //  std::cout << boost::format("   properties_sample_length = ") << _properties_sample_length << std::endl;
//  //  std::cout << boost::format("              tei_screening = ") << _tei_screening << std::endl;
//  //  std::cout << boost::format("                   nthreads = ") << _nthreads << std::endl;
//  //  std::cout << boost::format("           arena_block_size = ") << _arena_block_size << std::endl;
//  //  std::cout << boost::format("          arena_buffer_size = ") << _arena_buffer_size << std::endl;
//  //  std::cout << boost::format("                global_seed = ") << _global_seed << std::endl;
//  //  std::cout << boost::format("                   uhf_bias = ");
//  //  for (int i = 0; i < _uhf_bias.size(); i++)
//  //    std::cout << boost::format(" %5.2f") % _uhf_bias.at(i);
//  //  std::cout << std::endl;
//  //  std::cout << boost::format("             uhf_bias_decay = ") << _uhf_bias_decay << std::endl;
//  //  std::cout << boost::format("          uhf_desegragation = ") << _uhf_desegragation << std::endl;
//  //  std::cout << boost::format("         init_config_thresh = ") << _init_config_thresh << std::endl;
//  //  std::cout << boost::format("                init_config = ");
//  //  for (int i = 0; i < _init_config.size(); i++)
//  //    std::cout << boost::format(" %i") % _init_config.at(i);
//  //  std::cout << std::endl;
//  //  std::cout << std::endl;
//  //  std::cout << boost::format("Executing in %s parallel mode with %i processes, each running %i threads.")
//  //               % _parallel_type % nproc % _nthreads << std::endl;
//  //  std::cout << std::endl;
//  //  //std::cout << boost::format("Timings have an accuracy of %.1e seconds.") % MPI::Wtick() << std::endl;
//  //  //std::cout << std::endl;
//  //}
//
//}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Checks that the user input options are sane.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Input::verify() {

  // verify optimization type
  if (    _optimization != pcps::option_value::Opt_BFGS
       && _optimization != pcps::option_value::Opt_Steepest_Descent
       && _optimization != pcps::option_value::Opt_Projected
       && _optimization != pcps::option_value::Opt_Projected_TI
       && _optimization != pcps::option_value::Opt_Imaginary_Time
       && _optimization != pcps::option_value::Opt_Stochastic_Imaginary_Time
       && _optimization != pcps::option_value::Opt_Real_Time
       && _optimization != pcps::option_value::Opt_Full_CI
       && _optimization != pcps::option_value::Opt_PMC
       && _optimization != pcps::option_value::Opt_Dump_DMRG_Ints
       && _optimization != pcps::option_value::Opt_None      )
    throw pcps::Exception( (boost::format("option \"optimization\" must be \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::Opt_BFGS
                            % pcps::option_value::Opt_Steepest_Descent
                            % pcps::option_value::Opt_Projected
                            % pcps::option_value::Opt_Projected_TI
                            % pcps::option_value::Opt_Imaginary_Time
                            % pcps::option_value::Opt_Stochastic_Imaginary_Time
                            % pcps::option_value::Opt_Real_Time
                            % pcps::option_value::Opt_Full_CI
                            % pcps::option_value::Opt_PMC
                            % pcps::option_value::Opt_Dump_DMRG_Ints
                            % pcps::option_value::Opt_None
                           ).str() );

  // verify lattice type
  if (    _lattice_type != pcps::option_value::Lattice_Cartesian
       && _lattice_type != pcps::option_value::Lattice_Triangular
       && _lattice_type != pcps::option_value::Lattice_Icosidodecahedron
       && _lattice_type != pcps::option_value::Lattice_Orbitals )
    throw pcps::Exception( (boost::format("option \"lattice_type\" must be \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::Lattice_Cartesian
                            % pcps::option_value::Lattice_Triangular
                            % pcps::option_value::Lattice_Icosidodecahedron
                            % pcps::option_value::Lattice_Orbitals
                           ).str() );

  // verify correlator type
  if (    _correlator_type != pcps::option_value::Correlator_Line
       && _correlator_type != pcps::option_value::Correlator_Square
       && _correlator_type != pcps::option_value::Correlator_Triangle )
    throw pcps::Exception( (boost::format("option \"correlator_type\" must be \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::Correlator_Line
                            % pcps::option_value::Correlator_Square
                            % pcps::option_value::Correlator_Triangle
                           ).str() );

  // verify hamiltonian type
  if (   _hamiltonian_type != pcps::option_value::Ham_Hubbard
      && _hamiltonian_type != pcps::option_value::Ham_Heisenberg
      && _hamiltonian_type != pcps::option_value::Ham_File
      && _hamiltonian_type != pcps::option_value::Ham_PSI3
      && _hamiltonian_type != pcps::option_value::Ham_Molpro
      && _hamiltonian_type != pcps::option_value::Ham_Text_1212
      && _hamiltonian_type != pcps::option_value::Ham_Text_1122 )
    throw pcps::Exception("option \"hamiltonian_type\" must be \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::Ham_Hubbard
                            % pcps::option_value::Ham_Heisenberg
                            % pcps::option_value::Ham_File
                            % pcps::option_value::Ham_PSI3
                            % pcps::option_value::Ham_Molpro
                            % pcps::option_value::Ham_Text_1212
                            % pcps::option_value::Ham_Text_1122;

  // verify particle type
  if (    _particle_type != pcps::option_value::Part_Spin
       && _particle_type != pcps::option_value::Part_Spinless_Fermion
       && _particle_type != pcps::option_value::Part_Fermion )
    throw pcps::Exception( (boost::format("option \"particle_type\" must be \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::Part_Spin
                            % pcps::option_value::Part_Spinless_Fermion
                            % pcps::option_value::Part_Fermion
                           ).str() );

  // verify reference
  if (    _particle_type == pcps::option_value::Part_Fermion
       && _ref_type != pcps::option_value::Ref_Uniform
       && _ref_type != pcps::option_value::Ref_RHF
       && _ref_type != pcps::option_value::Ref_AGP
       && _ref_type != pcps::option_value::Ref_Pairing
       && _ref_type != pcps::option_value::Ref_Pfaffian )
    throw pcps::Exception( (boost::format("Error in \"ref_type\" option.  Available fermion references are \"%s\", \"%s\", \"%s\", \"%s\", and \"%s\".")
                            % pcps::option_value::Ref_Uniform
                            % pcps::option_value::Ref_RHF
                            % pcps::option_value::Ref_AGP
                            % pcps::option_value::Ref_Pairing
                            % pcps::option_value::Ref_Pfaffian
                           ).str() );
  if (    _particle_type == pcps::option_value::Part_Spinless_Fermion
       && _ref_type != pcps::option_value::Ref_Uniform
       && _ref_type != pcps::option_value::Ref_RHF )
    throw pcps::Exception( (boost::format("Error in \"ref_type\" option.  Available spinless fermion references are \"%s\" and \"%s\".")
                            % pcps::option_value::Ref_Uniform
                            % pcps::option_value::Ref_RHF
                           ).str() );
  if (    _particle_type == pcps::option_value::Part_Spin
       && _ref_type != pcps::option_value::Ref_Uniform 
       && _ref_type != pcps::option_value::Ref_AGP
       && _ref_type != pcps::option_value::Ref_Pairing )
    throw pcps::Exception( (boost::format("Error in \"ref_type\" option.  Available spin references are \"%s\", \"%s\", and \"%s\".")
                            % pcps::option_value::Ref_Uniform
                            % pcps::option_value::Ref_AGP
                            % pcps::option_value::Ref_Pairing
                           ).str() );

//  // verify correlator sources
//  for (std::set<std::string>::const_iterator s = _corr_sources.begin(); s != _corr_sources.end(); s++)
//    if (    *s != pcps::option_value::Corr_Source_None
//         && *s != pcps::option_value::Corr_Source_Binary
//         && *s != pcps::option_value::Corr_Source_Text
//         && *s != pcps::option_value::Corr_Source_Classical
//         && *s != pcps::option_value::Corr_Source_Lattice
//         && *s != pcps::option_value::Corr_Source_All_Pair )
//      throw pcps::Exception( (boost::format("option \"corr_sources\" may only contain \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\".")
//                              % pcps::option_value::Corr_Source_None
//                              % pcps::option_value::Corr_Source_Binary
//                              % pcps::option_value::Corr_Source_Text
//                              % pcps::option_value::Corr_Source_Classical
//                              % pcps::option_value::Corr_Source_Lattice
//                              % pcps::option_value::Corr_Source_All_Pair
//                             ).str() );

  // verify orbital source
  if (    _orb_source != pcps::option_value::Orb_Source_Binary
       && _orb_source != pcps::option_value::Orb_Source_Text
       && _orb_source != pcps::option_value::Orb_Source_None )
    throw pcps::Exception( (boost::format("option \"orb_source\" must be \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::Orb_Source_Binary
                            % pcps::option_value::Orb_Source_Text
                            % pcps::option_value::Orb_Source_None
                           ).str() );

  // verify agp weight source
  if (    _agp_source != pcps::option_value::AGP_Source_Binary
       && _agp_source != pcps::option_value::AGP_Source_Text
       && _agp_source != pcps::option_value::AGP_Source_RHF
       && _agp_source != pcps::option_value::AGP_Source_None )
    throw pcps::Exception( (boost::format("option \"agp_source\" must be \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::AGP_Source_Binary
                            % pcps::option_value::AGP_Source_Text
                            % pcps::option_value::AGP_Source_RHF
                            % pcps::option_value::AGP_Source_None
                           ).str() );

  // verify pairing matrix source
  if (    _pairing_source != pcps::option_value::Pairing_Source_Binary
       && _pairing_source != pcps::option_value::Pairing_Source_Text
       && _pairing_source != pcps::option_value::Pairing_Source_Random
       && _pairing_source != pcps::option_value::Pairing_Source_AGP
       && _pairing_source != pcps::option_value::Pairing_Source_GHF
       && _pairing_source != pcps::option_value::Pairing_Source_None )
    throw pcps::Exception( (boost::format("option \"pairing_source\" must be \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::Pairing_Source_Binary
                            % pcps::option_value::Pairing_Source_Text
                            % pcps::option_value::Pairing_Source_Random
                            % pcps::option_value::Pairing_Source_AGP
                            % pcps::option_value::Pairing_Source_GHF
                            % pcps::option_value::Pairing_Source_None
                           ).str() );

  // verify move type
  if (    _particle_type == pcps::option_value::Part_Fermion
       && _move_type != pcps::option_value::Move_Standard
       && _move_type != pcps::option_value::Move_Exchange
       && _move_type != pcps::option_value::Move_Mix
       && _move_type != pcps::option_value::Move_DoubleAB    )
    throw pcps::Exception( (boost::format("for fermions, option \"move_type\" must be \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::option_value::Move_Standard
                            % pcps::option_value::Move_Exchange
                            % pcps::option_value::Move_Mix
                            % pcps::option_value::Move_DoubleAB
                           ).str() );
  if (    _particle_type == pcps::option_value::Part_Spinless_Fermion
       && _move_type != pcps::option_value::Move_Standard    )
    throw pcps::Exception( (boost::format("for spinless fermions, option \"move_type\" must be \"%s\".")
                            % pcps::option_value::Move_Standard
                           ).str() );
  if (    _particle_type == pcps::option_value::Part_Spin
       && _move_type != pcps::option_value::Move_Standard    )
    throw pcps::Exception( (boost::format("for spins, option \"move_type\" must be \"%s\".")
                            % pcps::option_value::Move_Standard
                           ).str() );

  // verify particle spin
  if ( _particle_spin <= 0.0 || !pcps::is_half_integer(_particle_spin) )
    throw pcps::Exception("option \"particle_spin\" must be a positive half integer (0.5, 1.0, 1.5, ...)");
  if ( _particle_type == pcps::option_value::Part_Fermion && std::fabs(_particle_spin - 0.5) > 1.0e-9 )
    throw pcps::Exception("option \"particle_spin\" must be 0.5 for fermions");

  // verify z component of spin
  if (!pcps::is_half_integer(_z_spin))
    throw pcps::Exception("option \"z_spin\" must be a half integer (..., -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, ...)");

  // verify damping
  if (_damping < 0.0 || _damping >= 1.0)
    throw pcps::Exception("option \"damping\" must be in the range 0.0 <= damping < 1.0");

  // verify step_size
  if (_step_size <= 0.0)
    throw pcps::Exception("option \"step_size\" must be positive");

  // verify boundary type
  if (    _boundary != pcps::option_value::Boundary_Open
       && _boundary != pcps::option_value::Boundary_Periodic )
    throw pcps::Exception( (boost::format("option \"boundary\" must be \"%s\" or \"%s\".")
                            % pcps::option_value::Boundary_Open
                            % pcps::option_value::Boundary_Periodic
                           ).str() );

  // verify parallelism type
  if (    _parallel_type != pcps::option_value::Parallel_Site
       && _parallel_type != pcps::option_value::Parallel_Farm )
    throw pcps::Exception( (boost::format("option \"parallel_type\" must be \"%s\" or \"%s\".")
                            % pcps::option_value::Parallel_Site
                            % pcps::option_value::Parallel_Farm
                           ).str() );

  // verify scalar type
  if (    _scalar_type != pcps::option_value::Scalar_Real
       && _scalar_type != pcps::option_value::Scalar_Complex )
    throw pcps::Exception( (boost::format("option \"scalar_type\" must be \"%s\" or \"%s\".")
                            % pcps::option_value::Scalar_Real
                            % pcps::option_value::Scalar_Complex
                           ).str() );

  // verify number of threads
  if ( _nthreads <= 0 )
    throw pcps::Exception( (boost::format("option \"nthreads\" must be positive.")).str() );

  // verify arena block size
  if ( _arena_block_size < 0 )
    throw pcps::Exception( (boost::format("option \"arena_block_size\" must be non-negative.")).str() );

  // verify arena buffer size
  if ( _arena_buffer_size < 0 )
    throw pcps::Exception( (boost::format("option \"arena_buffer_size\" must be non-negative.")).str() );

  // verify that spin projection is allowed
  if ( !_project_sz && _particle_type != pcps::option_value::Part_Spin )
    throw pcps::Exception( (boost::format("option \"project_sz\" may be false ONLY if \"particle_type\" is set to %s.")
                            % pcps::option_value::Part_Spin
                           ).str() );

  // verify that AGP weight optimization is allowed
  if (    _optimize_agp_weights
       && _ref_type != pcps::option_value::Ref_AGP     )
    throw pcps::Exception( (boost::format("option \"optimize_agp_weights = true\" requires the \"%s\" reference type.")
                            %  pcps::option_value::Ref_AGP
                           ).str() );

  // verify that orbital coefficient optimization is allowed
  if (    _optimize_orb_coeffs
       && _ref_type != pcps::option_value::Ref_RHF
       && _ref_type != pcps::option_value::Ref_AGP     )
    throw pcps::Exception( (boost::format("option \"optimize_orb_coeffs = true\" requires either the \"%s\" or \"%s\" reference type.")
                            %  pcps::option_value::Ref_RHF
                            %  pcps::option_value::Ref_AGP
                           ).str() );

  // verify that pairing matrix optimization is allowed
  if (    _optimize_pairing_matrix
       && _ref_type != pcps::option_value::Ref_Pairing
       && _ref_type != pcps::option_value::Ref_Pfaffian )
    throw pcps::Exception( (boost::format("option \"optimize_pairing_matrix = true\" requires the \"%s\" or \"%s\" reference type.")
                            %  pcps::option_value::Ref_Pairing
                            %  pcps::option_value::Ref_Pfaffian
                           ).str() );

  // verify that pairing matrix symmetrization is allowed
  if ( _symmetric_pairing && ( _ref_type != pcps::option_value::Ref_Pairing || _optimize_pairing_matrix == false ) )
    throw pcps::Exception(
      (boost::format("option \"symmetric_pairing = true\" requires \"optimize_pairing_matrix = true\" and the \"%s\" reference type.")
        %  pcps::option_value::Ref_Pairing
      ).str() );

  // verify that a spin to fermion dump is allowed
  if ( _spin_to_fermion_dump && ( _particle_type != pcps::option_value::Part_Spin || std::fabs(_particle_spin - 0.5) > 1.0e-9 ) )
    throw pcps::Exception( (boost::format("option \"spin_to_fermion_dump = true\" requires spin 1/2 spins.")).str() );

  // verify that the projector monte carlo value of tau is positive
  if ( _pmc_tau <= 0.0 )
    throw pcps::Exception( (boost::format("option \"pmc_tau\" must be positive.")).str() );

  // verify that the two electron integral screeing threshold is non-negative
  if ( _tei_screening < 0.0 )
    throw pcps::Exception( (boost::format("option \"tei_screening\" must be non-negative.")).str() );

  // verify that the number of walkers per process is positive
  if ( _walkers_per_process <= 0 )
    throw pcps::Exception( (boost::format("option \"walkers_per_process\" must be positive.")).str() );

  // verify that the population control frequency is positive
  if ( _pop_control_freq <= 0 )
    throw pcps::Exception( (boost::format("option \"pop_control_freq\" must be positive.")).str() );

  // verify that the stochastic reconfiguration disk block size is positive
  if ( _sr_disk_block_size <= 0 )
    throw pcps::Exception( (boost::format("option \"sr_disk_block_size\" must be positive.")).str() );

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Prepares some data that is derived from the user's input options.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Input::initialize_derived_data() {

  //// if no correlator sources were specified, set the sources to "none"
  //if (_corr_sources.size() == 0)
  //  _corr_sources.insert(pcps::option_value::Corr_Source_None);

  // determine the number of sites
  if (_lattice_type == pcps::option_value::Lattice_Cartesian || _lattice_type == pcps::option_value::Lattice_Triangular)
    _nsites = _lattice_length_x * _lattice_length_y * _lattice_length_z;
  else if (_lattice_type == pcps::option_value::Lattice_Icosidodecahedron)
    _nsites = 30;
  else if (_lattice_type == pcps::option_value::Lattice_Orbitals)
    _nsites = _n_act_orb;
  else
    throw pcps::Exception("unknown lattice_type");

  // if the particles are fermions, double the number of sites to account for alpha and beta
  if (_particle_type == pcps::option_value::Part_Fermion)
    _nsites *= 2;

  // determine the number of alpha and beta electrons
  _n_elec_a = pcps::round( 0.5 * _nparticles + _z_spin );
  _n_elec_b = pcps::round( 0.5 * _nparticles - _z_spin );

  // ensure that the working directory ends in a slash
  if (_workdir.size() > 0) {
    if (_workdir[_workdir.size()-1] != '/')
      _workdir.push_back('/');
  } else {
    throw pcps::Exception("option \"workdir\" must not be blank");
  }

  // determine the number of configurations per site
  if (    _particle_type == pcps::option_value::Part_Fermion
       || _particle_type == pcps::option_value::Part_Spinless_Fermion )
    _configs_per_site = 2;
  else if ( _particle_type == pcps::option_value::Part_Spin )
    _configs_per_site = pcps::round(2 * _particle_spin) + 1;
  else
    throw pcps::Exception("unexpected particle type");

  // determine the number of agp weights
  if (      _particle_type == pcps::option_value::Part_Fermion )
    _n_agp_weights = _nsites/2;
  else if ( _particle_type == pcps::option_value::Part_Spin )
    _n_agp_weights = _nsites;
  else
    _n_agp_weights = 0;

  // determine the number of restricted orbital coefficients
  if (      _particle_type == pcps::option_value::Part_Fermion )
    _n_rest_orb_coeff = _nsites*_nsites/4;
  else if ( _particle_type == pcps::option_value::Part_Spin )
    _n_rest_orb_coeff = _nsites*_nsites;
  else
    _n_rest_orb_coeff = 0;

  // determine the number of variables in the pairing matrix
  if ( _ref_type == pcps::option_value::Ref_Pfaffian ) {
    if (      _particle_type == pcps::option_value::Part_Fermion)
      _n_pairing_variables = _nsites*(_nsites-1)/2;
    else if ( _particle_type == pcps::option_value::Part_Spin )
      _n_pairing_variables = 2*_nsites*(2*_nsites-1)/2;
    else
      _n_pairing_variables = 0;
  } else {
    if (      _particle_type == pcps::option_value::Part_Fermion)
      _n_pairing_variables = _nsites*_nsites/4;
    else if ( _particle_type == pcps::option_value::Part_Spin )
      _n_pairing_variables = _nsites*_nsites;
    else
      _n_pairing_variables = 0;
  }

  // if requested, use the current time for the global random seed
  if (_global_seed < 0)
    _global_seed = int( std::time(NULL) );
  if (_global_seed < 0)
    _global_seed = -_global_seed;

  // if the bias vector is the wrong size, resize it and fill it with zeros
  if (_uhf_bias.size() != _nsites/2)
    _uhf_bias.assign(_nsites/2, 0.0);

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Splits a string into pieces using the supplied deliminators.
///
/// \param[in]     str           The string to be split.
/// \param[in]     deliminators  Characters used to deliminate the sections the string is to
///                              be split into.
///
/// \return A vector holding the sections of the split string.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<std::string> pcps::split_string(const std::string &str, const std::string &deliminators) {

  // Initialize the return value
  std::vector<std::string> retval;

  // create a set of the deliminators
  std::set<char> delim_set;
  for (int i = 0; i < deliminators.size(); i++)
    delim_set.insert(deliminators.at(i));

  // Loop through the string searching for deliminating characters
  int i1 = 0;
  for (int i2 = 0; i2 < str.size(); i2++)
    // When the deliminating character is found, add a new string
    // to the return value consisting of all characters between the
    // previous and current deliminating characters
    if (delim_set.count(str.at(i2)))
    {
      if (i2 > i1)
        retval.push_back(str.substr(i1, i2-i1));
      i1 = i2 + 1;
    }

  // Add a string to the return value consisting of any characters between
  // the last deliminator and the end of the string
  if (i1 < str.size())
    retval.push_back(str.substr(i1, str.size()-i1));

  // return the vector of the split string
  return retval;

}

/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief   Broadcasts an input command and its line number to the slave processes.
/////          This function should only be called by the root process.
/////
///// \param[in]     cmd        the command read from the input file
///// \param[in]     line_num   the input file line number on which the command ended
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//void pcps::Input::bcast_input_command(std::vector<char> & cmd, int line_num) {
//
//  // broadcast the line number and the command
//  pcps::mpi::bcast(line_num);
//  pcps::mpi::bcast(cmd);
//
//}

/////////////////////////////////////////////////////////////////////////////////////////////////////
///// \brief   Reads the %name of a user input command and sets the corresponding member's value.
/////
///// \param[in]     cmd        the command read from the input file
///// \param[in]     line_num   the input file line number on which the command ended
/////
/////////////////////////////////////////////////////////////////////////////////////////////////////
//void pcps::Input::process_input_command(const std::vector<char> & cmd, const int line_num) {
//
//  // process the command, watching for errors
//  try {
//
//    // convert the command into a string stream,
//    // padding with a space to ensure that >> operators do not set eof prematurely
//    std::stringstream ss( std::string(cmd.begin(), cmd.end()).append(" ") );
//
//    // get the name of the member whose value the command is setting
//    std::string member_name;
//    ss >> member_name;
//
//    // if the line was empty, there is nothing to do
//    if (ss.eof()) return;
//
//    // if the member name is somehow empty, raise an error
//    if (member_name.empty())
//      throw pcps::Exception("member_name is empty");
//
//    // set the corresponding data member
//    this->set_member(member_name, ss);
//
//  // if an error occured, edit the error message to include the line number
//  } catch (std::exception & e) {
//
//    throw pcps::Exception("problem processing command ending on line %i:  %s") % line_num % e.what();
//
//  }
//
//}
